﻿using System;
using System.Collections.Concurrent;
using System.IO.Ports;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace Commnications
{
    public abstract class EasyCommBase : IEasyComm, IDisposable
    {
        #region socket helper begin
        protected EasyContext _context;
        /// <summary>
        /// 是否连接中
        /// </summary>
        public bool IsConnected
        {
            get
            {
                if (_context != null)
                {
                    if (_mode.HasFlag(Mode.Server))
                        return _mode.HasFlag(Mode.Opened);
                    else
                        return _context.IsConnected;
                }
                return false;
            }
        }
        /// <summary>
        /// 创建tcp并连接
        /// </summary>
        /// <param name="ip">ip地址</param>
        /// <param name="port">端口号</param>
        /// <param name="timeout">连接超时</param>
        /// <returns>成功则true，否则fasle</returns>
        public virtual bool Connect(string ip, int port, int timeout, Action<EasyContext> successAction = null)
        {
            return ConnectV2(ip, port, timeout, successAction).IsSuccess;
        }
        public virtual EasyResult ConnectV2(string ip, int port, int timeout, Action<EasyContext> successAction = null)
        {
            bool isOk = false;
            var context = EasyHelper.CreateTcpV2();
            if (EasyHelper.ConnectAsync(ip, port, timeout, context.Net))
            {
                if (context.RemoteEndPoint == null)
                    context.RemoteEndPoint = EasyHelper.GetIPEndPoint(ip, port);
                if (context.LocalEndPoint == null)
                    context.SetLocalEndPoint(context.Net.LocalEndPoint);
                _context = context;
                if (successAction != null)
                {
                    successAction(context);
                }
                isOk = true;
            }
            return new EasyResult(isOk);
        }
        /// <summary>
        /// 打开串口，先调用StartCom
        /// </summary>
        /// <returns>成功则true，否则fasle</returns>
        public virtual bool Open()
        {
            if (_context != null && _context.Serial != null)
            {
                _context.Serial.Open();
                return _context.Serial.IsOpen;
            }
            return false;
        }
        /// <summary>
        /// udp
        /// </summary>
        /// <param name="ip">本地ip地址</param>
        /// <param name="port">端口号</param>
        /// <param name="action">返回客户端是否中止，true:中止，false：继续</param>
        /// <returns>成功则true，否则fasle</returns>
        public virtual bool StartUdpServer(string ip, int port, Action<EasyContext> successAction = null,
            Func<EasyContext, byte[], int, EndPoint, ChannelType, bool> action = null, Action<string, int> logger = null)
        {
            _mode = Mode.Server;
            var context = EasyHelper.CreateUdpV2(ip, port, true);
            context.logger = logger;
            _context = context;
            if (successAction != null)
            {
                successAction(context);
            }
            if (action != null)
            {
                EndPoint clientEndPoint = new IPEndPoint(IPAddress.Any, 0);
                var buffer = new byte[context.ReceiveBufferSize];
                new Thread(() =>
                {
                    if (logger != null)
                        logger($"udp server {ip}:{port} start...", (int)LogLevel.Info);
                    string key = "";
                    while (!_isStoped)
                    {
                        try
                        {
                            key = "";
                            int receivedBytes = _context.Net.ReceiveFrom(
                                buffer,
                                buffer.Length,
                                SocketFlags.None,
                               ref clientEndPoint
                            );
                            if (clientEndPoint != null)
                            {
                                key = clientEndPoint.ToString();
                            }
                            if (!string.IsNullOrWhiteSpace(key) && !_clients.ContainsKey(key))
                            {
                                _clients[key] = new EasyContext(clientEndPoint, ChannelType.Udp);
                            }
                            if (action(_context, buffer, receivedBytes, clientEndPoint, ChannelType.Udp))
                            {
                                if (!string.IsNullOrWhiteSpace(key))
                                {
                                    _clients.TryRemove(key, out _);
                                }
                            }
                            Array.Clear(buffer, 0, receivedBytes);
                        }
                        catch (Exception ex)
                        {
                            if (logger != null)
                                logger($"udp server err:{ex}", (int)LogLevel.Error);
                            if (_isStoped)
                            {
                                if (!string.IsNullOrWhiteSpace(key))
                                {
                                    _clients.TryRemove(key, out _);
                                }
                                break;
                            }
                        }
                    }

                    if (logger != null)
                        logger($"udp server {ip}:{port} stoped...", (int)LogLevel.Info);
                }).Start();
            }
            if (action != null)
                _mode |= Mode.Opened;
            return context != null;
        }
        public virtual bool StartUdpClient(string ip, int port, Action<EasyContext> successAction = null)
        {
            var context = EasyHelper.CreateUdpV2(ip, port, false);
            _context = context;
            if (successAction != null)
            {
                successAction(context);
            }
            return context != null;
        }
        public virtual bool StartUdpClient(string ip, int port, string remoteIp, int remotePort, Action<EasyContext> successAction = null)
        {
            var context = EasyHelper.CreateUdpV2(ip, port, remoteIp, remotePort);
            _context = context;
            var isOk = context != null;
            if (successAction != null)
            {
                successAction(context);
            }
            return isOk;
        }

        /// <summary>
        /// serial
        /// </summary>
        /// <param name="portName">com口,ex:COM1</param>
        /// <param name="baudRate">波特率,ex:9600</param>
        /// <param name="parity">奇偶位,ex:None,Odd,Even,Mark,Space</param>
        /// <param name="dataBits">数据位,ex:8</param>
        /// <param name="stopBits">停止位,ex:None,One,Two,OnePointFive</param>
        /// <returns>成功则true，否则fasle</returns>
        public virtual bool StartCom(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, Action<EasyContext, ChannelType> action = null)
        {
            _mode = Mode.Server;
            var context = EasyHelper.CreateCom(portName, baudRate, parity, dataBits, stopBits, action);
            _context = context;
            if (action != null)
                _mode |= Mode.Opened;
            return true;
        }
        /// <summary>
        /// 释放资源
        /// </summary>
        public virtual void Close()
        {
            if (_context != null)
            {
                if (_context.Net != null)
                {
                    if (_context.IsConnected)
                    {
                        var socket = _context.Net;
                        socket.Shutdown(SocketShutdown.Both);
                        socket.Close();
                    }
                }
                else if (_context.Serial != null && _context.Serial.IsOpen)
                {
                    _context.Serial.Close();
                }
            }
        }
        public virtual void Dispose()
        {
            _isStoped = true;
            if (_context != null)
            {
                _context.Dispose();
            }
            this._context = null;
        }
        /// <summary>
        /// 设置读取超时（单位ms）
        /// </summary>
        public int ReadTimeout
        {
            set
            {
                var timeout = value;
                if (_context != null)
                {
                    if (_context.Net != null)
                    {
                        //if (_context.IsConnected)
                        //{
                        _context.Net.ReceiveTimeout = timeout;
                        //}
                    }
                    else if (_context.Serial != null /*&& _context.Serial.IsOpen*/)
                    {
                        _context.Serial.ReadTimeout = timeout;
                    }
                }
            }
            get
            {
                var timeout = 0;
                if (_context != null)
                {
                    if (_context.Net != null)
                    {
                        //if (_context.IsConnected)
                        //{
                        timeout = _context.Net.ReceiveTimeout;
                        //}
                    }
                    else if (_context.Serial != null /*&& _context.Serial.IsOpen*/)
                    {
                        timeout = _context.Serial.ReadTimeout;
                    }
                }
                return timeout;
            }
        }
        /// <summary>
        /// 设置写超时（单位ms）
        /// </summary>
        public int WriteTimeout
        {
            set
            {
                var timeout = value;
                if (_context != null)
                {
                    if (_context.Net != null)
                    {
                        //if (_context.IsConnected)
                        //{
                        _context.Net.SendTimeout = timeout;
                        //}
                    }
                    else if (_context.Serial != null /*&& _context.Serial.IsOpen*/)
                    {
                        _context.Serial.WriteTimeout = timeout;
                    }
                }
            }
            get
            {
                var timeout = 0;
                if (_context != null)
                {
                    if (_context.Net != null)
                    {
                        //if (_context.IsConnected)
                        //{
                        timeout = _context.Net.SendTimeout;
                        //}
                    }
                    else if (_context.Serial != null/* && _context.Serial.IsOpen*/)
                    {
                        timeout = _context.Serial.WriteTimeout;
                    }
                }
                return timeout;
            }
        }
        bool _isStoped = false;
        Mode _mode = Mode.Client;
        public long ClientCount => _clients.Count;
        ConcurrentDictionary<string, EasyContext> _clients = new ConcurrentDictionary<string, EasyContext>();
        public ConcurrentDictionary<string, EasyContext> Sessions => _clients;
        internal enum LogLevel
        {
            Trace, Debug, Info, Warn, Error, Fatal
        }
        public virtual bool StartTcpServer(int port, Action<EasyContext, ChannelType> action, int backlog = 10, string ip = null, Action<string, int> logger = null)
        {
            _mode = Mode.Server;
            if (action == null)
                throw new ArgumentNullException("action");
            var context = EasyHelper.CreateTcpServer(port, ip);
            context.logger = logger;
            _context = context;
            context.Listen(backlog);
            new Thread(() =>
            {
                if (logger != null)
                    logger($"tcp server {ip}:{port} start...", (int)LogLevel.Info);
                while (!_isStoped)
                {
                    try
                    {
                        Socket clientSocket = _context.Net.Accept();
                        // 为每个客户端创建独立任务处理通信（避免阻塞其他客户端）
                        Task.Factory.StartNew(() =>
                        {
                            var client = new EasyContext(clientSocket);
                            var key = clientSocket.RemoteEndPoint.ToString();
                            try
                            {
                                _clients[key] = client;
                                action(client, ChannelType.Tcp);
                            }
                            catch (Exception ex)
                            {
                                if (logger != null)
                                    logger($"client handle {clientSocket.RemoteEndPoint} err{ex}", (int)LogLevel.Error);
                                //Console.WriteLine($"客户端通信异常 {clientSocket.RemoteEndPoint}：{ex.Message}");
                            }
                            finally
                            {
                                _clients.TryRemove(key, out _);
                                // 关闭客户端Socket，释放资源
                                clientSocket.Shutdown(SocketShutdown.Both);
                                clientSocket.Close();
                                //Console.WriteLine($"客户端连接已关闭：{clientSocket.RemoteEndPoint}");
                            }
                        });
                    }
                    catch (Exception ex)
                    {
                        if (logger != null)
                            logger($"tcp server err:{ex}", (int)LogLevel.Error);
                        if (_isStoped)
                            break;
                    }
                }
                if (logger != null)
                    logger($"tcp server {ip}:{port} stoped...", (int)LogLevel.Info);
            }).Start();
            if (action != null)
                _mode |= Mode.Opened;
            return true;
        }
        #endregion socket helper end

        #region public interface begin
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="cmd">发送命令</param>
        /// <param name="len">长度</param>
        /// <returns>响应</returns>
        public virtual EasyResultT<byte[]> Read(byte[] cmd, int len, bool sendFirst = true)
        {
            var buffer = new byte[len];
            if (sendFirst)
                _context.Send(cmd);
            var recvLen = _context.Receive(buffer, len);
            return new EasyResultT<byte[]>(recvLen >= len, buffer);
        }
        /// <summary>
        /// 写数据
        /// </summary>
        /// <param name="cmd">发送命令</param>
        /// <returns>是否写命令成功</returns>
        public virtual EasyResult Write(byte[] cmd)
        {
            return new EasyResult(false);
        }
        /// <summary>
        /// 接收数据：
        /// udp先调用StartUdp；
        /// serial先调用StartCom；
        /// tcp先调用StartTcpServer；
        /// </summary>
        /// <param name="data">结果记录</param>
        //public virtual void OnDataArrive(EasyResult data)
        //{
        //}
        [Flags]
        enum Mode
        {
            Client,
            Server,
            Opened = 0x1000,
        }
        #endregion public interface end
    }
}
